import React, { useState, useEffect } from 'react';
import {
  Box,
  Table,
  TableBody,
  Button,
  IconButton,
  TableCell,
  TableHead,
  TableRow,
  TableSortLabel,
  TableContainer,
  Divider,
  Typography,
  TablePagination,
  styled,
} from '@mui/material';
import {
  ArrowBack as ArrowBackIcon,
  Code as CodeIcon,
  Replay as ReplayIcon,
  Assessment as AssessmentIcon,
  ArrowDropDown as ArrowDropDownIcon,
  FilterList as FilterListIcon,
} from '@mui/icons-material';
import { useAppDispatch } from '../../redux/hooks';
import { EXPERIMENT_TEXTS, EXPERIMENT_VIEWS, EXPERIMENT_STATE } from '../../constants';
import DialogBox from '../general/DialogBox';
import { viewExperimentResults, loadTrialModelSpec } from '../../redux/slices/experimentsSlice';
import * as ExperimentsApi from '../../api/experiments';
import Tooltip from '../general/Tooltip';
import {
  bgRegular,
  bgDarker,
  bgLight,
  canvasBgLight,
  bgInputs,
  primaryColor,
  experimentGrey,
  secondaryColor,
  fontColor,
} from '../../theme';
import ExperimentRowFilter from './ExperimentRowFilter';
import { stableSort, getComparator } from './utils';
import Loader from '../general/Loader';

const StyledRoot = styled(Box)(({ theme }) => ({
  width: 'auto !important',
  flexDirection: 'column',
  marginLeft: -theme.spacing(1),
  marginRight: -theme.spacing(1),

  '& .primary-loader': {
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'center',
    flexDirection: 'column',
    fontWeight: 500,
    color: fontColor,
    fontSize: '1rem',
    height: '30rem',
  },

  '& .editExperiment-filter': {
    display: 'flex',
    alignItems: 'center',

    '& .MuiSvgIcon-replay': {
      transform: 'rotate(-65deg)',
    },
    '& .MuiButton-root': {
      minWidth: 'inherit',
      marginLeft: theme.spacing(1.2),
    },
  },
  '& .MuiTablePagination-root': {
    overflow: 'initial',

    '& .MuiTypography-body2': {
      fontSize: '0.875rem',
    },
  },
  '& .MuiTablePagination-select': {
    fontSize: '0.875rem',
    color: experimentGrey,
    paddingTop: theme.spacing(0),
    paddingBottom: theme.spacing(0),
  },
  '& .MuiTablePagination-selectRoot': {
    '& .MuiSelect-icon': {
      color: experimentGrey,
    },
  },
  '& .MuiTablePagination-actions': {
    paddingRight: theme.spacing(3),
    '& .MuiIconButton-root': {
      padding: '0.25rem',
      marginLeft: theme.spacing(2.6),
      color: experimentGrey,
      '&.Mui-disabled': {
        opacity: 0.3,
      },
      '&:hover': {
        borderRadius: theme.spacing(0.4),
      },
    },
  },
  '& .ViewExperimentHead': {
    paddingLeft: theme.spacing(4),
    paddingRight: theme.spacing(4),
    display: 'flex',
    justifyContent: 'space-between',
    alignItems: 'center',
  },
  '& .editExperiment-trials': {
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'flex-end',
    background: bgDarker,
    padding: theme.spacing(1, 4),
    '& .MuiTypography-h5': {
      fontSize: '1rem',
      color: fontColor,
      marginLeft: theme.spacing(1),
      marginRight: theme.spacing(0.5),
    },
  },
  '& .editExperimentBack': {
    display: 'flex',
    cursor: 'pointer',
    '& .MuiTypography-root': {
      marginLeft: theme.spacing(1),
    },
  },
  '& .MuiTypography-body2': {
    fontSize: '1rem',
  },
  '& .MuiDivider-vertical': {
    height: theme.spacing(4),
    background: 'rgba(255, 255, 255, 0.3)',
  },
  '& .MuiTableContainer-root': {
    '&::-webkit-scrollbar-thumb': {
      background: secondaryColor,
      height: theme.spacing(0.8),
    },
    '&::-webkit-scrollbar': {
      width: theme.spacing(0.8),
      height: theme.spacing(0.8),
    },
    '& .MuiTableCell-root': {
      padding: theme.spacing(1.4, 4),
      '&:first-child': {
        '& .MuiSortIcon': {
          display: 'none',
        },
      },
    },
    '& .MuiTableRow-colspan': {
      '& .MuiTableCell-root': {
        background: bgDarker,
        padding: theme.spacing(1),
      },
      '& .MuiBox-root': {
        display: 'flex',
        alignItems: 'center',
        fontSize: '0.75rem',
        fontWeight: 500,
        color: experimentGrey,
        '& .MuiSvgIcon-root': {
          fontSize: '0.813rem',
          marginRight: theme.spacing(1),
        },
      },
    },
    '& .MuiTableCell-actions': {
      display: 'flex',
      alignItems: 'center',
      justifyContent: 'flex-end',
      '& .MuiIconButton-root': {
        padding: '0.25rem',
        marginLeft: theme.spacing(2.6),
        color: experimentGrey,
        '&:hover': {
          borderRadius: theme.spacing(0.4),
        },
      },
      '& .MuiDivider-root': {
        marginLeft: theme.spacing(2.6),
      },
      '& .MuiSvgIcon-assessment': {
        color: primaryColor,
      },
      '& .MuiSvgIcon-replay': {
        fontSize: '1.2rem',
        transform: 'rotate(-65deg)',
      },
    },
    '& .MuiTableRow-head': {
      '& .MuiTableSortLabel-icon': {
        display: 'none',
      },
      '& .MuiSortIcon': {
        '&[direction="desc"]': {
          transform: 'rotate(180deg)',
        },
      },
      '& .MuiTableCell-root': {
        background: bgLight,
        color: experimentGrey,
        fontWeight: 700,
        whiteSpace: 'nowrap',
        borderBottom: `1px solid ${bgInputs}`,
      },
    },
    '& .MuiTableRow-root': {
      '& .MuiTableCell-root': {
        color: experimentGrey,
        fontWeight: 700,
        background: bgRegular,
        fontSize: '0.875rem',
      },
    },
    '& .MuiTableBody-root': {
      '& .MuiTableCell-root': {
        '&.MuiTableCell-paddingNone': {
          color: canvasBgLight,
          fontSize: '1rem',
          fontWeight: 500,
        },
      },
    },
  },
  '& .sticky': {
    position: 'sticky',
    left: 0,
    zIndex: 50,
    background: bgRegular,
  },
  '& .stickyRight': {
    position: 'sticky',
    right: 0,
    textAlign: 'right',
    background: bgRegular,
    '& .MuiSortIcon': {
      display: 'none',
    },
  },
}));

interface ParamHeader {
  id: string;
  numeric: boolean;
  disablePadding: boolean;
  label: string;
}

interface TableRow {
  id: number;
  name: string;
  [key: string]: any;
}

interface Filter {
  param: string;
  value: string;
}

interface Experiment {
  name: string;
  state: string;
  trials: Array<{
    id: number;
    name: string;
    params: Array<Record<string, any>>;
  }>;
}

interface EnhancedTableHeadProps {
  order: 'asc' | 'desc';
  orderBy: string;
  onRequestSort: (event: React.MouseEvent<unknown>, property: string) => void;
  paramHeaders: ParamHeader[];
  experimentFinished: boolean;
}

/**
 * Enhanced Table Head Component
 */
const EnhancedTableHead: React.FC<EnhancedTableHeadProps> = ({
  order,
  orderBy,
  onRequestSort,
  paramHeaders,
  experimentFinished,
}) => {
  const createSortHandler = (property: string) => (event: React.MouseEvent<unknown>) => {
    onRequestSort(event, property);
  };

  const headCells = [
    {
      id: 'name',
      numeric: false,
      disablePadding: true,
      label: 'EXPERIMENTAL CONDITIONS',
    },
    ...paramHeaders,
  ];

  if (experimentFinished) {
    headCells.push({
      id: 'status',
      numeric: false,
      disablePadding: false,
      label: 'Actions',
    });
  }

  return (
    <TableHead>
      <TableRow>
        {headCells.map((headCell, index) => (
          <TableCell
            key={headCell.id}
            align="left"
            padding={headCell.disablePadding ? 'none' : 'normal'}
            sortDirection={orderBy === headCell.id ? order : false}
            className={index === 0 ? 'sticky' : headCell.id === 'status' ? 'stickyRight' : ''}
          >
            <TableSortLabel
              active={orderBy === headCell.id}
              onClick={createSortHandler(headCell.id)}
            >
              {headCell.label}
              <ArrowDropDownIcon
                className="MuiSortIcon"
                sx={{
                  transform: orderBy === headCell.id && order === 'desc' ? 'rotate(180deg)' : 'none',
                }}
              />
            </TableSortLabel>
          </TableCell>
        ))}
      </TableRow>
    </TableHead>
  );
};

interface ExperimentViewProps {
  name: string;
  setTrial: (trial: string) => void;
  setTrialJSON: (json: any) => void;
  setView: (view: string) => void;
}

/**
 * ExperimentView Component
 * Displays experiment trials in a sortable, filterable table
 */
const ExperimentView: React.FC<ExperimentViewProps> = ({
  name,
  setTrial,
  setTrialJSON,
  setView,
}) => {
  const dispatch = useAppDispatch();
  const [order, setOrder] = useState<'asc' | 'desc'>('asc');
  const [orderBy, setOrderBy] = useState<string>('name');
  const [tableRows, setTableRows] = useState<TableRow[]>([]);
  const [filteredRows, setFilteredRows] = useState<TableRow[]>(tableRows);
  const [experiment, setExperiment] = useState<Experiment | null>(null);
  const [paramHeaders, setParamHeaders] = useState<ParamHeader[]>([]);
  const [page, setPage] = useState(0);
  const [rowsPerPage, setRowsPerPage] = useState(10);
  const [loadResultsDialogOpen, setLoadResultsDialogOpen] = useState(false);
  const [loadModelSpecDialogOpen, setLoadModelSpecDialogOpen] = useState(false);
  const [selectedTrial, setSelectedTrial] = useState<{
    experiment: string | null;
    trial: TableRow | null;
  }>({
    experiment: null,
    trial: null,
  });
  const [loading, setLoading] = useState(false);

  const experimentFinished =
    experiment?.state === EXPERIMENT_STATE.SIMULATED ||
    experiment?.state === EXPERIMENT_STATE.INSTANTIATED;

  const handleRequestSort = (event: React.MouseEvent<unknown>, property: string): void => {
    const isAsc = orderBy === property && order === 'asc';
    setOrder(isAsc ? 'desc' : 'asc');
    setOrderBy(property);
  };

  const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);
  const popoverhandleClick = (event: React.MouseEvent<HTMLElement>): void => {
    setAnchorEl(event.currentTarget);
  };

  const filterObj: Filter = {
    param: '',
    value: '',
  };
  const [filter, setFilter] = useState<Filter[]>([filterObj]);

  const addFilterRow = (): void => {
    setFilter([...filter, filterObj]);
  };

  const filterRows = (arr: TableRow[], criteria: Filter[]): TableRow[] => {
    const filterWithoutEmpties = criteria.filter((f) => f.param && f.value);
    if (filterWithoutEmpties.length > 0) {
      const matchesFilter = (item: TableRow): boolean => {
        let count = 0;
        filterWithoutEmpties.forEach((f) => {
          if (Number(f.value) === item[f.param]) {
            count += 1;
          }
        });
        return count === filterWithoutEmpties.length;
      };
      return arr.filter((item) => matchesFilter(item));
    }
    return arr;
  };

  const setParameterValue = (val: string, index: number): void => {
    const newFilter = [...filter];
    newFilter[index] = {
      ...filter[index],
      value: val,
    };
    setPage(0);
    setFilter(newFilter);
    setFilteredRows(filterRows(tableRows, newFilter));
  };

  const filterParameterChange = (val: string, index: number): void => {
    const newFilter = [...filter];
    newFilter[index] = {
      param: val,
      value: '',
    };
    setFilter(newFilter);
  };

  useEffect(() => {
    if (name) {
      setLoading(true);
      ExperimentsApi.getExperiment(name)
        .then((exp) => {
          setExperiment(exp);
          if (exp?.trials && exp.trials.length > 0) {
            if (exp.trials[0].params && exp.trials[0].params.length > 0) {
              setParamHeaders(
                Object.keys(exp.trials[0].params[0]).map((header) => ({
                  id: header,
                  numeric: true,
                  disablePadding: false,
                  label: header,
                })),
              );
            }

            const rows: TableRow[] = exp.trials.map((trial) => ({
              id: trial.id,
              name: trial.name,
              ...(trial.params && trial.params.length > 0 ? trial.params[0] : {}),
            }));

            setTableRows(rows);
            setFilteredRows(rows);
          }
          setLoading(false);
        })
        .catch((error) => {
          console.error(error);
          setLoading(false);
        });
    }
  }, [name]);

  const openJsonViewer = (experimentName: string, trial: TableRow): void => {
    setLoading(true);
    ExperimentsApi.getModelSpecification(experimentName, trial.id)
      .then((modelSpecification) => {
        setTrialJSON(modelSpecification);
        setTrial(trial.name);
        setLoading(false);
        setView(EXPERIMENT_VIEWS.jsonViewer);
      })
      .catch((error) => {
        console.error(error);
        setLoading(false);
      });
  };

  const handleChangePage = (event: unknown, newPage: number): void => {
    setPage(newPage);
  };

  const handleChangeRowsPerPage = (event: React.ChangeEvent<HTMLInputElement>): void => {
    setRowsPerPage(parseInt(event.target.value, 10));
    setPage(0);
  };

  const removeFilter = (filterIndex: number): void => {
    const newFilter = [...filter];
    newFilter.splice(filterIndex, 1);
    setPage(0);
    setFilter(newFilter);
    setFilteredRows(filterRows(tableRows, newFilter));
  };

  const openLoadResultsDialog = (experimentName: string, trial: TableRow): void => {
    setSelectedTrial({
      experiment: experimentName,
      trial,
    });
    setLoadResultsDialogOpen(true);
  };

  const openLoadModelSpecificationDialog = (experimentName: string, trial: TableRow): void => {
    setSelectedTrial({
      experiment: experimentName,
      trial,
    });
    setLoadModelSpecDialogOpen(true);
  };

  const onLoadResultsAction = (actionConfirmed: boolean): void => {
    if (actionConfirmed && selectedTrial.experiment && selectedTrial.trial) {
      dispatch(
        viewExperimentResults({
          name: selectedTrial.experiment,
          trial: selectedTrial.trial,
        }),
      );
    }
    setSelectedTrial({
      experiment: null,
      trial: null,
    });
    setLoadResultsDialogOpen(false);
  };

  const onLoadModelSpecificationAction = (actionConfirmed: boolean): void => {
    if (actionConfirmed && selectedTrial.experiment && selectedTrial.trial) {
      dispatch(
        loadTrialModelSpec({
          name: selectedTrial.experiment,
          trial: selectedTrial.trial,
          onlyModelSpecification: true,
        }),
      );
    }
    setSelectedTrial({
      experiment: null,
      trial: null,
    });
    setLoadModelSpecDialogOpen(false);
  };

  return (
    <StyledRoot>
      <Box className="ViewExperimentHead">
        <Box mt={2} mb={3} className="editExperimentBack">
          <ArrowBackIcon onClick={() => setView(EXPERIMENT_VIEWS.list)} sx={{ cursor: 'pointer' }} />
          <Typography variant="body2">{experiment?.name}</Typography>
        </Box>
        <div className="editExperiment-filter">
          {/* Future: Replay button could be added here */}
        </div>
      </Box>
      {loading ? (
        <Box className="primary-loader">
          <Loader />
          Loading ...
        </Box>
      ) : (
        experiment?.trials && (
          <>
            <Box className="editExperiment-trials">
              <div className="editExperiment-filter">
                <Button onClick={popoverhandleClick}>
                  <FilterListIcon />
                  <Typography variant="h5">Filters</Typography>
                </Button>
                <ExperimentRowFilter
                  filter={filter}
                  paramHeaders={paramHeaders}
                  setParameterValue={setParameterValue}
                  filterParameterChange={filterParameterChange}
                  anchorEl={anchorEl}
                  setAnchorEl={setAnchorEl}
                  addFilterRow={addFilterRow}
                  removeFilter={removeFilter}
                />
              </div>
            </Box>
            <TableContainer>
              <Table>
                <EnhancedTableHead
                  order={order}
                  orderBy={orderBy}
                  onRequestSort={handleRequestSort}
                  paramHeaders={paramHeaders}
                  experimentFinished={experimentFinished}
                />
                <TableBody>
                  {stableSort(filteredRows, getComparator(order, orderBy))
                    .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
                    .map((row) => (
                      <TableRow tabIndex={-1} key={row.name}>
                        <TableCell
                          component="th"
                          scope="row"
                          padding="none"
                          className="sticky"
                        >
                          {row.name}
                        </TableCell>
                        {paramHeaders.map((header, index) => (
                          <TableCell key={`${header.label}_${index}`}>
                            {row[header.label]}
                          </TableCell>
                        ))}
                        {experimentFinished && (
                          <TableCell align="right" className="stickyRight">
                            <Box className="MuiTableCell-actions">
                              <Tooltip title="Explore results" aria-label="explore-results">
                                <IconButton
                                  onClick={() =>
                                    openLoadResultsDialog(experiment.name || '', row)
                                  }
                                >
                                  <AssessmentIcon className="MuiSvgIcon-assessment" sx={{ color: primaryColor }} />
                                </IconButton>
                              </Tooltip>
                              <Divider orientation="vertical" />
                              <Tooltip title="View model" aria-label="view-model">
                                <IconButton
                                  onClick={() => openJsonViewer(experiment.name || '', row)}
                                >
                                  <CodeIcon />
                                </IconButton>
                              </Tooltip>
                              <Tooltip
                                title="Load model specification"
                                aria-label="load-model-spec"
                              >
                                <IconButton
                                  onClick={() =>
                                    openLoadModelSpecificationDialog(experiment.name || '', row)
                                  }
                                >
                                  <ReplayIcon className="MuiSvgIcon-replay" />
                                </IconButton>
                              </Tooltip>
                            </Box>
                          </TableCell>
                        )}
                      </TableRow>
                    ))}
                </TableBody>
              </Table>
            </TableContainer>
            {filteredRows.length > 0 && (
              <TablePagination
                rowsPerPageOptions={[5, 10, 25]}
                component="div"
                count={filteredRows.length}
                rowsPerPage={rowsPerPage}
                page={page}
                onPageChange={handleChangePage}
                onRowsPerPageChange={handleChangeRowsPerPage}
              />
            )}
            <DialogBox
              open={loadResultsDialogOpen}
              onDialogResponse={onLoadResultsAction}
              textForDialog={{
                heading: EXPERIMENT_TEXTS.VIEW_EXPERIMENTS_RESULTS,
                content: EXPERIMENT_TEXTS.VIEW_EXPERIMENTS_RESULTS_MESSAGE,
              }}
            />
            <DialogBox
              open={loadModelSpecDialogOpen}
              onDialogResponse={onLoadModelSpecificationAction}
              textForDialog={{
                heading: EXPERIMENT_TEXTS.LOAD_TRIAL_MODEL_SPEC,
                content: EXPERIMENT_TEXTS.LOAD_TRIAL_MODEL_SPEC_MESSAGE,
              }}
            />
          </>
        )
      )}
    </StyledRoot>
  );
};

export default ExperimentView;

